

#include "mygl.h"

const float g_ViewNear = 0.1;
const float g_ViewFar = 10000.0;
const float g_ViewFOV = 45.0;


void glDefaultSetup()
{
	glClearColor(0, 0, 0, 0);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	glFrontFace(GL_CCW);
	glEnable(GL_NORMALIZE);
	glEnable(GL_COLOR_MATERIAL);
	//glEnable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDepthFunc(GL_LEQUAL);
    glAlphaFunc(GL_GREATER, 0.1);    // for trees
    glShadeModel(GL_SMOOTH);
    //light
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    float lightPos[] = {0.0, 0.0, 10.0, 1.0};
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
}

void glRotateq(const quatf& q)
{
    static matrix4f m;
    q.getMatrix(m);
    glMultMatrix(m);
}

static const float verBox[] = {
    -0.5, -0.5,  0.0,
     0.5, -0.5,  0.0,
    -0.5,  0.5,  0.0,
     0.5,  0.5,  0.0,
    -0.5, -0.5,  1.0,
     0.5, -0.5,  1.0,
    -0.5,  0.5,  1.0,
     0.5,  0.5,  1.0
};
#define ver(i)   glVertex3fv(&verBox[i*3])
void glRenderBox(float size_x, float size_y, float size_z)
{
    glPushMatrix();
    glScalef(size_x, size_y, size_z);
    glBegin(GL_QUADS);
        //bottom (3)
        glNormal3f(0.0, 0.0, -1.0);
        glTexCoord2f(0,0); ver(0);
        glTexCoord2f(0,1); ver(2);
        glTexCoord2f(1,1); ver(3);
        glTexCoord2f(1,0); ver(1);
        //top (4)
        glNormal3f(0.0, 0.0, 1.0);
        glTexCoord2f(0,0); ver(5);
        glTexCoord2f(0,1); ver(7);
        glTexCoord2f(1,1); ver(6);
        glTexCoord2f(1,0); ver(4);
        //back (5)
        glNormal3f(0.0, 1.0, 0.0);
        glTexCoord2f(1,0); ver(2);
        glTexCoord2f(1,1); ver(6);
        glTexCoord2f(0,1); ver(7);
        glTexCoord2f(0,0); ver(3);
        //front (2)
        glNormal3f(0.0, -1.0, 0.0);
        glTexCoord2f(1,1); ver(4);
        glTexCoord2f(1,0); ver(0);
        glTexCoord2f(0,0); ver(1);
        glTexCoord2f(0,1); ver(5);
        //left (6)
        glNormal3f(-1.0, 0.0, 0.0);
        glTexCoord2f(0,0); ver(0);
        glTexCoord2f(0,1); ver(4);
        glTexCoord2f(1,1); ver(6);
        glTexCoord2f(1,0); ver(2);
        //right (1)
        glNormal3f(1.0, 0.0, 0.0);
        glTexCoord2f(0,1); ver(5);
        glTexCoord2f(0,0); ver(1);
        glTexCoord2f(1,0); ver(3);
        glTexCoord2f(1,1); ver(7);
    glEnd();
    glPopMatrix();
}
void glRenderBoxCM(float size_x, float size_y, float size_z, const Color cmat[4])
{
    glPushMatrix();
    glScalef(size_x, size_y, size_z);
    glBegin(GL_QUADS);
        //top (4)
        glNormal3f(0.0, 0.0, 1.0);
        glColor(cmat[1]); glTexCoord2f(0,0); ver(5);
        glColor(cmat[2]); glTexCoord2f(0,1); ver(7);
        glColor(cmat[3]); glTexCoord2f(1,1); ver(6);
        glColor(cmat[0]); glTexCoord2f(1,0); ver(4);
        //back (5)
        glNormal3f(0.0, 1.0, 0.0);
        glColor(cmat[3]); glTexCoord2f(1,0); ver(2);
        glColor(cmat[3]); glTexCoord2f(1,1); ver(6);
        glColor(cmat[2]); glTexCoord2f(0,1); ver(7);
        glColor(cmat[2]); glTexCoord2f(0,0); ver(3);
        //front (2)
        glNormal3f(0.0, -1.0, 0.0);
        glColor(cmat[0]); glTexCoord2f(1,1); ver(4);
        glColor(cmat[0]); glTexCoord2f(1,0); ver(0);
        glColor(cmat[1]); glTexCoord2f(0,0); ver(1);
        glColor(cmat[1]); glTexCoord2f(0,1); ver(5);
        //left (6)
        glNormal3f(-1.0, 0.0, 0.0);
        glColor(cmat[0]); glTexCoord2f(0,0); ver(0);
        glColor(cmat[0]); glTexCoord2f(0,1); ver(4);
        glColor(cmat[3]); glTexCoord2f(1,1); ver(6);
        glColor(cmat[3]); glTexCoord2f(1,0); ver(2);
        //right (1)
        glNormal3f(1.0, 0.0, 0.0);
        glColor(cmat[1]); glTexCoord2f(0,1); ver(5);
        glColor(cmat[1]); glTexCoord2f(0,0); ver(1);
        glColor(cmat[2]); glTexCoord2f(1,0); ver(3);
        glColor(cmat[2]); glTexCoord2f(1,1); ver(7);
    glEnd();
    glPopMatrix();
}
#undef ver

static const float verCube[] = {
    -0.5, -0.5, -0.5,
     0.5, -0.5, -0.5,
    -0.5,  0.5, -0.5,
     0.5,  0.5, -0.5,
    -0.5, -0.5,  0.5,
     0.5, -0.5,  0.5,
    -0.5,  0.5,  0.5,
     0.5,  0.5,  0.5
};
#define ver(i)   glVertex3fv(&verCube[i*3])
void glRenderCube(float size_x, float size_y, float size_z)
{
    glPushMatrix();
    glScalef(size_x, size_y, size_z);
    glBegin(GL_QUADS);
        //bottom (3)
        glNormal3f(0.0, 0.0, -1.0);
        glTexCoord2f(0,0); ver(0);
        glTexCoord2f(0,1); ver(2);
        glTexCoord2f(1,1); ver(3);
        glTexCoord2f(1,0); ver(1);
        //top (4)
        glNormal3f(0.0, 0.0, 1.0);
        glTexCoord2f(0,0); ver(5);
        glTexCoord2f(0,1); ver(7);
        glTexCoord2f(1,1); ver(6);
        glTexCoord2f(1,0); ver(4);
        //back (5)
        glNormal3f(0.0, 1.0, 0.0);
        glTexCoord2f(1,0); ver(2);
        glTexCoord2f(1,1); ver(6);
        glTexCoord2f(0,1); ver(7);
        glTexCoord2f(0,0); ver(3);
        //front (2)
        glNormal3f(0.0, -1.0, 0.0);
        glTexCoord2f(1,1); ver(4);
        glTexCoord2f(1,0); ver(0);
        glTexCoord2f(0,0); ver(1);
        glTexCoord2f(0,1); ver(5);
        //left (6)
        glNormal3f(-1.0, 0.0, 0.0);
        glTexCoord2f(0,0); ver(0);
        glTexCoord2f(0,1); ver(4);
        glTexCoord2f(1,1); ver(6);
        glTexCoord2f(1,0); ver(2);
        //right (1)
        glNormal3f(1.0, 0.0, 0.0);
        glTexCoord2f(0,1); ver(5);
        glTexCoord2f(0,0); ver(1);
        glTexCoord2f(1,0); ver(3);
        glTexCoord2f(1,1); ver(7);
    glEnd();
    glPopMatrix();
}


void glRenderRect(float w, float h)
{
    glBegin(GL_QUADS);
    glNormal3f(0.0, 0.0, 1.0);
    glTexCoord2f(0,0); glVertex2f(-w/2, -h/2);
    glTexCoord2f(0,1); glVertex2f(w/2, -h/2);
    glTexCoord2f(1,1); glVertex2f(w/2, h/2);
    glTexCoord2f(1,0); glVertex2f(-w/2, h/2);
    glEnd();
}

void glRenderRectCM(float w, float h, const Color cmat[4])
{
    glBegin(GL_QUADS);
    glNormal3f(0.0, 0.0, 1.0);
    glColor(cmat[0]); glTexCoord2f(0,0); glVertex2f(-w/2, -h/2);
    glColor(cmat[1]); glTexCoord2f(0,1); glVertex2f(w/2, -h/2);
    glColor(cmat[2]); glTexCoord2f(1,1); glVertex2f(w/2, h/2);
    glColor(cmat[3]); glTexCoord2f(1,0); glVertex2f(-w/2, h/2);
    glEnd();
}

vector3f glGetPoint3D(int x, int y)
{
    double modelview[16], projection[16];
    int viewport[4];
    float z;
	double vx, vy, vz;

    glGetDoublev( GL_PROJECTION_MATRIX, projection );
    glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
    glGetIntegerv( GL_VIEWPORT, viewport );

    glReadPixels( x, viewport[3]-y, 1, 1,
		 GL_DEPTH_COMPONENT, GL_FLOAT, &z );

    gluUnProject( x, viewport[3]-y, z, modelview,
		projection, viewport, &vx, &vy, &vz );

	return vector3f(vx, vy, vz);
}


vector2f glGetPoint2D(vector3f pos)
{
    double modelview[16], projection[16];
    int viewport[4];
	double vx, vy, vz;

    glGetDoublev( GL_PROJECTION_MATRIX, projection );
    glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
    glGetIntegerv( GL_VIEWPORT, viewport );

    gluProject(pos.x, pos.y, pos.z, modelview, projection, viewport, &vx, &vy, &vz);

    return vector2f(vx, viewport[3] - vy);
}


//
// Scene 2D
//

void glBeginScene2D(float width, float height)
{
	//attribs
	glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_CULL_FACE);
	glColor3f(1, 1, 1);
	//projection
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(-width / 2, width / 2, -height / 2, height / 2);
	//view
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
}


void glEndScene2D()
{
	glPopAttrib();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}


//
// Scene 3D
//

void glBeginScene3D()
{
	//attribs
	glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT);
	glDisable(GL_BLEND);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);
	glEnable(GL_CULL_FACE);
	glColor3f(1, 1, 1);
	//calculate aspect
	int view[4];
	glGetIntegerv(GL_VIEWPORT, view);
	float aspect = (float)view[2]/(float)view[3];
	//perspective
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluPerspective(g_ViewFOV, aspect, g_ViewNear, g_ViewFar);
	//view
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
}


void glEndScene3D()
{
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glPopAttrib();
	//glMatrixMode(GL_MODELVIEW);
}


//
// Scene GUI
//

void glBeginSceneGUI(int width, int height)
{
	//attribs
	glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_CULL_FACE);
	glColor3f(1, 1, 1);
	//projection
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(0, width, height, 0);
	//view
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
}


void glEndSceneGUI()
{
	glPopAttrib();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}


void glEnableFog(Color c, int filter, float start, float end)
{
	GLuint fogMode[] = { GL_EXP, GL_EXP2, GL_LINEAR };	// Storage For Three Types Of Fog
	GLfloat fogColor[4] = {c.getRf(), c.getGf(), c.getBf(), c.getAf()};		// Fog Color

	glClearColor(fogColor[0], fogColor[1], fogColor[2], fogColor[3]);	// We'll Clear To The Color Of The Fog ( Modified )

	glFogi(GL_FOG_MODE, fogMode[filter]);		// Fog Mode
	glFogfv(GL_FOG_COLOR, fogColor);			// Set Fog Color
	glFogf(GL_FOG_DENSITY, 0.05f);				// How Dense Will The Fog Be
	glHint(GL_FOG_HINT, GL_DONT_CARE);			// Fog Hint Value
	glFogf(GL_FOG_START, start);				// Fog Start Depth
	glFogf(GL_FOG_END, end);				// Fog End Depth
	glEnable(GL_FOG);					// Enables GL_FOG
}


void glDrawRect(const rectf& r, const Color& color)
{
	glPushAttrib(GL_CURRENT_BIT);
	glColor(color);
	glBegin(GL_LINE_LOOP);
	glVertex2f(r.AA.x, r.AA.y);
	glVertex2f(r.BB.x, r.AA.y);
	glVertex2f(r.BB.x, r.BB.y);
	glVertex2f(r.AA.x, r.BB.y);
	glEnd();
	glPopAttrib();
}

void glFillRect(const rectf& r, const Color& color)
{
	glPushAttrib(GL_CURRENT_BIT);
	glColor(color);
	glBegin(GL_QUADS);
	glVertex2f(r.AA.x, r.AA.y);
	glVertex2f(r.BB.x, r.AA.y);
	glVertex2f(r.BB.x, r.BB.y);
	glVertex2f(r.AA.x, r.BB.y);
	glEnd();
	glPopAttrib();
}

void glFillRectCM(const rectf& r, const Color cmat[4])
{
	glPushAttrib(GL_CURRENT_BIT);
	glBegin(GL_QUADS);
	glColor(cmat[0]); glVertex2f(r.AA.x, r.AA.y);
	glColor(cmat[1]); glVertex2f(r.BB.x, r.AA.y);
	glColor(cmat[2]); glVertex2f(r.BB.x, r.BB.y);
	glColor(cmat[3]); glVertex2f(r.AA.x, r.BB.y);
	glEnd();
	glPopAttrib();
}


void glDrawCircle(float r, const Color& color, int segments)
{
	glPushAttrib(GL_CURRENT_BIT);
	glColor(color);
	glBegin(GL_LINE_LOOP);
    for (int i = 0; i < segments; ++i)
    {
        glVertex2f(cos((float)i / (float)segments * PI * 2) * r,
                   sin((float)i / (float)segments * PI * 2) * r);
    }
	glEnd();
	glPopAttrib();
}


//------------
// picking...
//------------

#define BUFSIZE 512
GLuint selectBuf[BUFSIZE];

static int processHits(GLint hits, GLuint buffer[])
{
    unsigned int i, j;
    GLuint names, *ptr, minZ,*ptrNames, numberOfNames;

    ptr = (GLuint *) buffer;
    minZ = 0xffffffff;
    for (i = 0; i < (GLuint)hits; i++)
    {
        names = *ptr;
        ptr++;
        if (*ptr < minZ)
        {
            numberOfNames = names;
            minZ = *ptr;
            ptrNames = ptr+2;
        }
        ptr += names+2;
    }

    int pp;
    ptr = ptrNames;
    for (j = 0; j < numberOfNames; j++,ptr++)
    {
        pp = *ptr;
    }

    //picked object (pp)
    return pp;
}

void glBeginPicking(int cursorX, int cursorY, float sense)
{
	int view[4];
	glGetIntegerv(GL_VIEWPORT, view);
	float aspect = (float)view[2]/(float)view[3];

    glSelectBuffer(BUFSIZE,selectBuf);
    glRenderMode(GL_SELECT);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();

    gluPickMatrix(cursorX, view[3]-cursorY, sense, sense, view);
	gluPerspective(g_ViewFOV, aspect, g_ViewNear, g_ViewFar);
    glMatrixMode(GL_MODELVIEW);
    glInitNames();
}

int glEndPicking()
{
    int hits;

    // restoring the original projection matrix
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glFlush();

    // returning to normal rendering mode
    hits = glRenderMode(GL_RENDER);

    // if there are hits process them
    if (hits != 0)
        return processHits(hits,selectBuf);

    return 0;
}


